import java.util.ArrayList;

/**
 * This class will hold game menu objects. This is the main menu driver. It
 * calls methods of the game and handles players interaction with the game.
 * 
 * @author michal
 * 
 */
public class GameMenu {
  Game    game;
  boolean specialRound;
  int     specialRoundCount;

  /**
   * Constructor for objects of class GameMenu. Creates new game object
   * 
   */
  public GameMenu() {
    this.game = new Game();
  }

  /**
   * Starting method .
   * 
   * @param args
   */
  public static void main(String[] args) {

    GameMenu app = new GameMenu();
    app.mainRun();
  }

  /**
   * mainMenu() prints out main menu, added DEV mode for testing purposes.
   * 
   * @return
   */
  private int mainMenu() {
    StdOut.println("-------Main Menu------");
    StdOut.println("----------------------");
    StdOut.println("1. Add new Player");
    StdOut.println("   No. of Players: " + game.players.size());
    StdOut.println("2. List players");
    // TO DO:
    // removePlayer
    // editPlayer
    StdOut.println("3. Start new game");
    StdOut.println("0. Exit");
    StdOut.println("****DEV MODE****");
    // StdOut.println("7. Dice roll");
    StdOut.println("8. Load Players");
    StdOut.println("9. Save players");

    StdOut.println("==>>");
    int option = StdIn.readInt();
    StdIn.readLine();
    return option;
  }

  /**
   * mainRun() Handles players inputs to navigate in the game.
   * 
   */
  @SuppressWarnings("unchecked")
  private void mainRun() {
    int option = mainMenu();
    while (option != 0) {
      switch (option) {
      case 1:
        game.addPlayer(); // add player to the game
        break;
      case 2:
        game.listPlayers(); // lists players currently addded
        break;
      case 3:
        startNewGame(); // to start new game, saving/loading might be
                        // implemented
        break;
      case 7:
        // game.diceRoll(); // Dev Test. diceroll()
        break;
      case 8:
        // Load player
        game.players = (ArrayList<Player>) StdStream
            .readFromFile("Players.xml");
        break;
      case 9:
        // Save players
        StdStream.saveToFile(game.players, "Players.xml");
        break;

      }
      option = mainMenu();
    }
    StdOut.println("Exiting... bye");
    System.exit(0);
  }

  /**
   * gameMenu() Prints menu layout for beggining of the game menu.
   * 
   * @return
   */
  private int gameMenu() {
    StdOut.println("------Game Menu-------");
    StdOut.println("----------------------");
    StdOut.println("1. Begin special purchase round");
    StdOut.println("5. Back to main menu");
    StdOut.println("----------------------");
    // StdOut.println("****DEV MODE****");
    // StdOut.println("8. Load game");
    // StdOut.println("9. Save game");

    StdOut.println("==>>");
    int option = StdIn.readInt();
    StdIn.readLine();
    return option;
  }

  /**
   * gameRun() Handles players inputs to navigate in the game
   * 
   * 
   */
  private void gameRun() {
    int option = gameMenu();
    while (option != 0) {
      switch (option) {
      case 1:
        specialRound = true; // special buying round, all players take turns in
                             // spending that initial 200m cash they re getting
        specialRoundCount = 1; // special round counter to ensure proper flow of
                               // the game and available menu options
        game.turn = 1; // game turn count, might be implemented for statistics
                       // reasons
        assetsRun();
        break;
      case 5:
        mainRun();
        break;
      case 8:
        // load game
        Game loadGame = (Game) StdStream.readFromFile("game.xml");
        setGame(loadGame);
        break;
      case 9:
        // save game
        StdStream.saveToFile(game, "game.xml");
        break;
      }
      option = gameMenu();
    }
  }

  /**
   * assetsMenu() Prints asset menu system, handles special round management
   * 
   * @return
   */
  private int assetsMenu() {
    StdOut.println("------Asset Menu------");
    StdOut.println("----------------------");
    StdOut.println("1. Buy an asset");
    if (!specialRound) { // if it is not specialRound allow to sell assets
      StdOut.println("2. Sell an asset");
    }
    StdOut.println("3. Check your board");
    // left in case of implementing neighbor phase
    // if (specialRound || game.declaredVictory
    // || game.currentPlayer.playerNumber == game.players.size()) {
    if (game.finalRoundCounter == game.players.size() && game.declaredVictory) {
      StdOut.println("4. End Game");
    } else {
      StdOut.println("4. Next Player");
    }
    // left in case of implementing neighbor phase
    // } else if (!specialRound && specialRoundCount == game.players.size()) {
    // StdOut.println("4. Next phase");
    // }
    StdOut.println("5. Current Prices");
    if (game.currentPlayer.cash >= 750) {
      StdOut.println("6. Declare victory");
    }
    // else if (game.finalRoundCounter == game.players.size()
    // && game.declaredVictory) {
    // StdOut.println("6. End Game");
    // }
    StdOut.println("0. Back to game menu");
    currentPlayerInfo();
    StdOut.println("----------------------");

    StdOut.println("==>>");
    int option = StdIn.readInt();
    StdIn.readLine();
    return option;
  }

  /**
   * assetsRun() Handles player inputs to navigate in the game.
   * 
   */
  private void assetsRun() {
    int option = assetsMenu();
    while (option != 0) {
      switch (option) {
      case 1:
        buyAssetRun();
        break;
      case 2:
        if (!specialRound) {
          sellAssetRun();
        }
        break;
      case 3:
        game.currentPlayer.playerBoard.printBoard();
        break;
      case 4:
        // if all players completed special round set specialround false and go
        // to market phase (which is proper first phase in regular game)
        if (game.finalRoundCounter == game.players.size()
            && game.declaredVictory) {
          game.endGame();
        } else if (specialRound && specialRoundCount == game.players.size()) {
          specialRound = false;
          game.nextPlayer();
          marketRun();
          // if not all players completed special round allow to switch to next
          // player increment specialRpundCounter by 1
        } else if (specialRound && specialRoundCount < game.players.size()) {
          specialRoundCount++;
          game.nextPlayer();
          assetsRun();
          // if it is not special round next phase is production phase / dice
          // roll
        } else if (!specialRound) {
          game.nextPlayer();
          marketRun();
        }
        break;
      case 5:
        currentAssetPrices();
        break;
      case 6:
        if (game.currentPlayer.cash >= 750) {
          game.declaredVictory = true;
          game.declaringPlayer = game.currentPlayer.playerNumber;
          game.finalRoundCounter = 1;
        }
        break;
      }
      option = assetsMenu();
    }
    option = gameMenu();
  }

  /**
   * buyAssetMenu() Displays menu that allows player to buy assets,
   * 
   * @return
   */
  public int buyAssetMenu() {
    // buying assets menu,
    // print current prices, give an option to pick a asset, and then place it
    // on the board
    // might reuse the same menu for every building.
    // big chunk of this method can be reused in selling
    StdOut.println("----Buy Assets Menu---");
    StdOut.println("----------------------");
    currentAssetPrices();
    StdOut.println("----------------------");
    StdOut.println("1. Buy a Drilling Rig ");
    StdOut.println("2. Buy a Gas station");
    StdOut.println("3. Buy a Refinery");
    StdOut.println("4. View your game board");
    StdOut.println("0. Back to assets menu");
    currentPlayerInfo();
    StdOut.println("----------------------");

    StdOut.println("==>>");
    int option = StdIn.readInt();
    StdIn.readLine();
    return option;
  }

  /**
   * buyAssetRun() manages players input to navigate in the game, does
   * validations for correct field and checks if player can afford to buy
   * choosen asset
   */
  public void buyAssetRun() {
    String type;
    int xRef;
    int yRef;
    int option = buyAssetMenu();
    while (option != 0) {
      switch (option) {
      case 1:
        // if player can afford drilling rig
        if (game.currentPlayer.cash > game.currentEconomy.drillingRigBuy) {
          xRef = getHorReference();
          yRef = getVerReference();
          type = "drillingRig";
          // if xRef and yRef are not -1, AND field referenced is empty.
          if (game.currentPlayer.playerBoard.asets[xRef][yRef]
              .equals(GameBoard.empty)
              && xRef != -1 && yRef != -1) {
            game.buyAsset(type, xRef, yRef);
          } else {
            StdOut.println("Sorry but this field is already ocupied");
          }
        } else {
          StdOut.println("Sorry but you can`t afford to buy this asset");
        }
        break;
      case 2:
        if (game.currentPlayer.cash > game.currentEconomy.gasStationBuy) {
          xRef = getHorReference();
          yRef = getVerReference();
          type = "gasStation";

          // if xRef and yRef are not -1, AND field referenced is empty.
          if (game.currentPlayer.playerBoard.asets[xRef][yRef]
              .equals(GameBoard.empty)
              && xRef != -1 && yRef != -1) {
            game.buyAsset(type, xRef, yRef);
          } else {
            StdOut.println("Sorry but this field is already ocupied");
          }
        } else {
          StdOut.println("Sorry but you can`t afford to buy this asset");
        }
        break;
      case 3:
        if (game.currentPlayer.cash > game.currentEconomy.refineryBuy) {
          xRef = getHorReference();
          yRef = getVerReference();
          type = "refinery";

          // if xRef and yRef are not -1, AND field referenced is empty.
          if (game.currentPlayer.playerBoard.asets[xRef][yRef]
              .equals(GameBoard.empty)
              && xRef != -1 && yRef != -1) {
            game.buyAsset(type, xRef, yRef);
          } else {
            StdOut.println("Sorry but this field is already ocupied");
          }
        } else {
          StdOut.println("Sorry but you can`t afford to buy this asset");
        }
        break;
      case 4:
        game.currentPlayer.playerBoard.printBoard();
        break;
      }
      option = buyAssetMenu();
    }
    option = assetsMenu();
  }

  /**
   * sellAssetMenu() Displays menu that allows player to sell assets.
   * 
   * @return
   */
  private int sellAssetMenu() {
    StdOut.println("---Sell Assets Menu---");
    currentAssetPrices();
    StdOut.println("----------------------");
    StdOut.println("1. Sell a Drilling Rig ");
    StdOut.println("2. Sell a Gas station");
    StdOut.println("3. Sell a Refinery");
    StdOut.println("4. Sell a Refinery");
    StdOut.println("5. View your game board");
    StdOut.println("0. Back to assets menu");
    currentPlayerInfo();
    StdOut.println("----------------------");

    StdOut.println("==>>");
    int option = StdIn.readInt();
    StdIn.readLine();
    return option;
  }

  /**
   * sellAssetRun() Handles players inputs to navigate in sell assets menu.
   * 
   */
  private void sellAssetRun() {
    String type;
    int xRef;
    int yRef;
    int option = sellAssetMenu();
    while (option != 0) {
      switch (option) {
      case 1:
        if (game.currentPlayer.nrOfDrillingRigs > 0) {
          xRef = getHorReference();
          yRef = getVerReference();
          type = "drillingRig";

          // if xRef and yRef are not -1 AND field is a drilling rig.
          if (game.currentPlayer.playerBoard.asets[xRef][yRef]
              .equals(GameBoard.drillingRig)
              && xRef != -1 && yRef != -1) {
            game.sellAsset(type, xRef, yRef);
          } else {
            StdOut.println("Sorry but you ve choosen wrong field");
          }
        } else {
          StdOut.println("Sorry but you don`t have any assets of this type");
        }
        break;
      case 2:
        if (game.currentPlayer.nrOfGasStations > 0) {
          xRef = getHorReference();
          yRef = getVerReference();
          type = "gasStation";

          // if xRef and yRef are not -1 AND field is a gas station.
          if (game.currentPlayer.playerBoard.asets[xRef][yRef]
              .equals(GameBoard.gasStation)
              && xRef != -1 && yRef != -1) {
            game.sellAsset(type, xRef, yRef);
          } else {
            StdOut.println("Sorry but you ve choosen wrong field");
          }
        } else {
          StdOut.println("Sorry but you don`t have any assets of this type");
        }
        break;
      case 3:
        if (game.currentPlayer.nrOfRefineries > 0) {
          xRef = getHorReference();
          yRef = getVerReference();
          type = "refinery";

          // if xRef and yRef are not -1 AND field is a refinery.
          if (game.currentPlayer.playerBoard.asets[xRef][yRef]
              .equals(GameBoard.refinery)
              && xRef != -1 && yRef != -1) {
            game.sellAsset(type, xRef, yRef);
          } else {
            StdOut.println("Sorry but you ve choosen wrong field");
          }
        } else {
          StdOut.println("Sorry but you don`t have any assets of this type");
        }
        break;
      case 4:
        if (game.currentPlayer.nrOfOilWells > 0) {
          xRef = getHorReference();
          yRef = getVerReference();
          type = "oilWell";

          // if xRef and yRef are not -1 AND field is a oil well.
          if (game.currentPlayer.playerBoard.asets[xRef][yRef]
              .equals(GameBoard.oilWell)
              && xRef != -1 && yRef != -1) {
            game.sellAsset(type, xRef, yRef);
          } else {
            StdOut.println("Sorry but you ve choosen wrong field");
          }
        } else {
          StdOut.println("Sorry but you don`t have any assets of this type");
        }
        break;
      case 5:
        game.currentPlayer.playerBoard.printBoard();
        break;
      }
      option = sellAssetMenu();
    }
    option = assetsMenu();
  }

  /**
   * marketMenu() Displays market menu view.
   * 
   * @return
   */
  private int marketMenu() {
    StdOut.println("-----Market Menu------");
    StdOut.println("----------------------");
    StdOut.println("1. Domestic market");
    StdOut.println("2. Foreign Market");
    StdOut.println("3. Next Phase");
    currentPlayerInfo();
    StdOut.println("----------------------");
    StdOut.println("0. Return");

    StdOut.println("==>>");
    int option = StdIn.readInt();
    StdIn.readLine();
    return option;
  }

  /**
   * marketRun() handles player input to navigate in market menu.
   * 
   */
  private void marketRun() {
    int option = marketMenu();
    while (option != 0) {
      switch (option) {
      case 1:
        domesticRun();
        break;
      case 2:
        foreignRun();
        break;
      case 3:
        productionRun();
        break;
      }
      option = marketMenu();
    }
  }

  /**
   * foreignMenu() Displays menu for foreign market view. Allows player to buy
   * and sell on foreign market.
   * 
   * @return
   */
  private int foreignMenu() {
    StdOut.println("---Foreign Market Menu---");
    StdOut.println("-------------------------");
    StdOut.println("Current Prices: ");
    StdOut.println("Crude Oil: " + game.ecoBoard.getOilBuyForeign());
    StdOut.println("Gasoline : " + game.ecoBoard.getGasBuyForeign());
    // if player havn`t bought/sold on domestic market
    if (!game.domestic) {
      if (!game.middleEast_44) {
        StdOut.println("1. Buy  1 Crude oil");
      }
      StdOut.println("2. Sell 1 Crude Oil");
      StdOut.println("3. Buy 1 Gasoline");
      StdOut.println("4. Sell 1 Gasoline");
    } else if (game.domestic) {
      StdOut.println("You can only do buisnees in one market type per turn");
    }
    currentPlayerInfo();
    StdOut.println("----------------------");
    StdOut.println("0. Back to Market Menu");

    StdOut.println("==>>");
    int option = StdIn.readInt();
    StdIn.readLine();
    return option;
  }

  /**
   * foreignRun() handles players input to navigate foreign market menu
   * 
   */
  private void foreignRun() {
    int option = foreignMenu();
    while (option != 0) {
      switch (option) {

      case 1:
        if (!game.domestic
            && game.currentPlayer.cash > game.ecoBoard.buyOilForeign
            && !game.middleEast_44) {
          game.buyOilForeign();
        } else if (game.middleEast_44) {
          StdOut
              .println("Market closed due war, sorry for inconvenience caused");
        } else {
          StdOut.println("Yer cannot afford it "
              + game.currentPlayer.playerName);
        }
        break;
      case 2:
        if (!game.domestic && game.currentPlayer.crudeOilBarrels > 0) {
          game.sellOilForeign();
        } else {
          StdOut.println("Yer have nothing to sale "
              + game.currentPlayer.playerName);
        }
        break;
      case 3:
        if (!game.domestic
            && game.currentPlayer.cash > game.ecoBoard.buyGasForeign) {
          game.buyGasolineForeign();
        } else {
          StdOut.println("Yer cannot afford it "
              + game.currentPlayer.playerName);
        }
        break;
      case 4:
        if (!game.domestic && game.currentPlayer.gassolineBarrels > 0) {
          game.sellGasolineForeign();
        } else {
          StdOut.println("Yer have nothing to sale "
              + game.currentPlayer.playerName);
        }
        break;
      }
      option = foreignMenu();
    }
    option = marketMenu();
  }

  /**
   * domesticMenu() displays domestic market menu view. Allows player to buy and
   * sell on domestic market
   * 
   * @return
   */
  private int domesticMenu() {
    StdOut.println("--Domestic Market Menu--");
    StdOut.println("------------------------");
    StdOut.println("Current Prices: ");
    StdOut.println("Crude Oil: " + game.ecoBoard.getOilBuyDomestic());
    StdOut.println("Gasoline : " + game.ecoBoard.getGasBuyDomestic());
    // if player havn`t bought/sold on foreign market
    if (!game.foreign) {
      StdOut.println("1. Buy 1 Crude oil");
      StdOut.println("2. Sell 1 Crude Oil");
      StdOut.println("3. Buy 1 Gasoline");
      StdOut.println("4. Sell 1 Gasoline");
    } else if (game.foreign) {
      StdOut.println("You can only do buisnees in one market type per turn");
    }
    currentPlayerInfo();
    StdOut.println("----------------------");
    StdOut.println("0. Back to Market Menu");

    StdOut.println("==>>");
    int option = StdIn.readInt();
    StdIn.readLine();
    return option;
  }

  /**
   * domesticRun() handles players input to navigate domestic market menu
   * 
   */
  private void domesticRun() {
    int option = domesticMenu();
    while (option != 0) {
      switch (option) {
      case 1:
        if (!game.foreign
            && game.currentPlayer.cash > game.ecoBoard.buyOilDomestic) {
          game.buyOilDomestic();
        } else {
          StdOut.println("Yer cannot afford it "
              + game.currentPlayer.playerName);
        }
        break;
      case 2:
        if (!game.foreign && game.currentPlayer.crudeOilBarrels > 0) {
          game.sellOilDomestic();
        } else {
          StdOut.println("Yer have nothing to sale "
              + game.currentPlayer.playerName);
        }
        break;
      case 3:
        if (!game.foreign
            && game.currentPlayer.cash > game.ecoBoard.buyGasDomestic) {
          game.buyGasolineDomestic();
        } else {
          StdOut.println("Yer cannot afford it "
              + game.currentPlayer.playerName);
        }
        break;
      case 4:
        if (!game.foreign && game.currentPlayer.gassolineBarrels > 0) {
          game.sellGasolineDomestic();
        } else {
          StdOut.println("Yer have nothing to sale "
              + game.currentPlayer.playerName);
        }
        break;
      }
      option = domesticMenu();
    }
    option = marketMenu();
  }

  /**
   * @return
   */
  private int productionMenu() {
    StdOut.println("----Production Menu---");
    StdOut.println("----------------------");
    if (!game.currentPlayer.rolled) {
      StdOut.println("1. Roll the Dice");// at this stage:
                                         // reset hit count to 0
                                         // check for double news
                                         // dice difference track
    }
    if (game.currentPlayer.rolled) {
      StdOut.println("You rolled:");
      StdOut.println("Black die: |" + game.xDie + "| Red die: |" + game.yDie
          + "|");

      StdOut.println("News event: " + game.newsCall(game.xDie, game.yDie));
      StdOut.println("2. Check for hits");
    }
    StdOut.println("3. Next phase");
    StdOut.println("----------------------");
    currentPlayerInfo();

    StdOut.println("==>>");
    int option = StdIn.readInt();
    StdIn.readLine();
    return option;
  }

  private void productionRun() {
    int option = productionMenu();
    while (option != 0) {
      switch (option) {
      case 1:
        if (!game.currentPlayer.rolled) {
          game.diceRoll();
        }
        break;
      case 2:
        if (!game.checkedHits) {
          game.checkHits();
        } else {
          StdOut.println("Already checked");
        }

        break;
      case 3:
        assetsRun();
        break;
      }
      option = productionMenu();
    }
  }

  protected void startNewGame() {
    game.currentPlayer = game.players.get(0); // setting current player to the
                                              // first player
    if (game.players.size() > 1) {
      gameRun();
    } else {
      StdOut.println("Not enough players to start the game.");
      mainMenu();
    }
  }

  /**
   * @return the game
   */
  public Game getGame() {
    return game;
  }

  /**
   * @param game
   *          the game to set
   */
  public void setGame(Game game) {
    this.game = game;
  }

  private int getHorReference() {
    game.currentPlayer.playerBoard.printBoard();
    StdOut.println("Please enter horizontal grid reference: ");
    int xRef = StdIn.readInt() - 1; // -1 as an array starts from 0
    if (xRef < 7 && xRef > -1) { // between 1 and 6
      return xRef;
    } else {
      StdOut.println("Invalid grid reference Captain Major Oil" + " \n ");
      return getHorReference();
    }
  }

  private int getVerReference() {
    // game.currentPlayer.playerBoard.printBoard();
    StdOut.println("Please enter vertical grid reference: ");
    int yRef = StdIn.readInt() - 1; // -1 as an array starts from 0
    if (yRef < 7 && yRef > -1) { // between 1 and 6
      return yRef;
    } else {
      StdOut.println("Invalid grid reference Captain Major Oil" + " \n ");
      return getVerReference();
    }
  }

  private void currentPlayerInfo() {
    StdOut.println("--------------------------");
    StdOut.println("Player: " + game.currentPlayer.playerName);
    StdOut.println("Cash: " + game.currentPlayer.cash);
    StdOut.println("Current economy: " + game.currentEconomy.economy);
  }

  private void currentAssetPrices() {
    StdOut.println("Current Prices: ");
    StdOut.println("Drilling Rig: Buy: "
        + game.currentEconomy.getDrillingRigBuy() + " Sell: "
        + game.currentEconomy.getDrillingRigSell());
    StdOut.println("Gas Station:  Buy: "
        + game.currentEconomy.getGasStationBuy() + " Sell: "
        + game.currentEconomy.getGasStationSell());
    StdOut.println("Refinery:     Buy: " + game.currentEconomy.getRefineryBuy()
        + " Sell: " + game.currentEconomy.getRefinerySell());
    StdOut.println("Gas Station:  Buy: "
        + game.currentEconomy.getGasStationBuy() + " Sell: "
        + game.currentEconomy.getGasStationSell());
    StdOut.println("Oil Well:     Sell: "
        + game.currentEconomy.getOilWellSell());
    StdOut.println("----------------------");
  }
}
